use crate::phi::data::Rectangle;
use crate::phi::gfx::{CopySprite, Sprite};
use crate::phi::{Phi, View, ViewAction};
use crate::views::DEBUG;
use sdl2::pixels::Color;
use sdl2::render::Renderer;

const PLAYER_SPEED: f64 = 180.0;
const SHIP_W: f64 = 43.0;
const SHIP_H: f64 = 39.0;

#[derive(Clone)]
pub struct Background {
    pub pos: f64,
    pub vel: f64,
    pub sprite: Sprite,
}

impl Background {
    pub(crate) fn render(&mut self, canvas: &mut Renderer, elapsed: f64) {
        let (w, h) = self.sprite.size();
        self.pos += self.vel * elapsed;

        if self.pos > w {
            self.pos -= w;
        }

        let (win_w, win_h) = canvas.output_size().unwrap();
        let scale = win_h as f64 / h;
        let mut physical_left = -self.pos * scale;
        while physical_left < win_w as f64 {
            canvas.copy_sprite(
                &self.sprite,
                Rectangle {
                    x: physical_left,
                    y: 0.0,
                    w: w * scale,
                    h: win_h as f64,
                },
            );
            physical_left += w * scale;
        }
    }
}

#[derive(Clone, Copy)]
enum ShipFrame {
    UpNorm = 0,
    UpFast = 1,
    UpSlow = 2,
    MidNorm = 3,
    MidFast = 4,
    MidSlow = 5,
    DownNorm = 6,
    DownFast = 7,
    DownSlow = 8,
}

struct Ship {
    rect: Rectangle,
    sprites: Vec<Sprite>,
    current: ShipFrame,
}

pub struct ShipView {
    player: Ship,
    bg_back: Background,
    bg_middle: Background,
    bg_front: Background,
}

impl ShipView {
    pub fn new(phi: &mut Phi) -> Self {
        let sprite_sheet =
            Sprite::load(&phi.canvas, "assets/spaceship.png").unwrap();
        let mut sprites = Vec::with_capacity(9);
        for y in 0..3 {
            for x in 0..3 {
                sprites.push(
                    sprite_sheet
                        .region(Rectangle {
                            w: SHIP_W,
                            h: SHIP_H,
                            x: SHIP_W * x as f64,
                            y: SHIP_H * y as f64,
                        })
                        .unwrap(),
                )
            }
        }
        Self {
            player: Ship {
                rect: Rectangle {
                    x: 64.0,
                    y: 64.0,
                    w: 32.0,
                    h: 32.0,
                },
                sprites,
                current: ShipFrame::MidNorm,
            },
            bg_front: Background {
                pos: 0.0,
                vel: 80.0,
                sprite: Sprite::load(&mut phi.canvas, "assets/star_fg.png")
                    .unwrap(),
            },
            bg_middle: Background {
                pos: 0.0,
                vel: 40.0,
                sprite: Sprite::load(&mut phi.canvas, "assets/star_mg.png")
                    .unwrap(),
            },
            bg_back: Background {
                pos: 0.0,
                vel: 20.0,
                sprite: Sprite::load(&mut phi.canvas, "assets/star_bg.png")
                    .unwrap(),
            },
        }
    }
}

impl View for ShipView {
    fn render(&mut self, context: &mut Phi, elapsed: f64) -> ViewAction {
        let (w, h) = context.output_size();
        let canvas = &mut context.canvas;
        let events = &context.events;

        if events.now.quit || events.now.key_escape == Some(true) {
            return ViewAction::Quit;
        }

        let diagonal: bool = (events.key_up ^ events.key_down)
            && (events.key_left ^ events.key_right);
        let moved = if diagonal { 1.0 / 2.0f64.sqrt() } else { 1.0 }
            * PLAYER_SPEED
            * elapsed;
        let dx: f64 = match (events.key_left, events.key_right) {
            (true, true) | (false, false) => 0.0,
            (true, false) => -moved,
            (false, true) => moved,
        };

        let dy: f64 = match (events.key_up, events.key_down) {
            (true, true) | (false, false) => 0.0,
            (true, false) => -moved,
            (false, true) => moved,
        };

        self.player.rect.x += dx;
        self.player.rect.y += dy;

        canvas.set_draw_color(Color::RGB(0, 0, 0));
        canvas.clear();

        self.bg_back.render(canvas, elapsed);
        self.bg_middle.render(canvas, elapsed);

        if DEBUG {
            canvas.set_draw_color(Color::RGB(200, 200, 50));
            canvas
                .fill_rect(self.player.rect.to_sdl().unwrap())
                .expect("");
        }

        let movable_region: Rectangle = Rectangle::new(0.0, 0.0, w * 0.7, h);
        self.player.rect =
            self.player.rect.move_inside(movable_region).unwrap();

        self.player.current = if dx == 0.0 && dy < 0.0 {
            ShipFrame::UpNorm
        } else if dx > 0.0 && dy < 0.0 {
            ShipFrame::UpFast
        } else if dx < 0.0 && dy < 0.0 {
            ShipFrame::UpSlow
        } else if dx == 0.0 && dy == 0.0 {
            ShipFrame::MidNorm
        } else if dx > 0.0 && dy == 0.0 {
            ShipFrame::MidFast
        } else if dx < 0.0 && dy == 0.0 {
            ShipFrame::MidSlow
        } else if dx == 0.0 && dy > 0.0 {
            ShipFrame::DownNorm
        } else if dx > 0.0 && dy > 0.0 {
            ShipFrame::DownFast
        } else if dx < 0.0 && dy > 0.0 {
            ShipFrame::DownSlow
        } else {
            unreachable!()
        };

        canvas.copy_sprite(
            &self.player.sprites[self.player.current as usize],
            self.player.rect,
        );

        self.bg_front.render(canvas, elapsed);
        ViewAction::None
    }
}
